package com.jzoom.zoom.admin.models;

import com.jzoom.zoom.admin.entities.DecoTableVo;
import com.jzoom.zoom.admin.entities.DecoTableVo.DecoColumn;
import com.jzoom.zoom.admin.entities.DecoTableVo.Link;
import com.jzoom.zoom.caster.Caster;
import com.jzoom.zoom.common.annotations.Inject;
import com.jzoom.zoom.common.json.JSON;
import com.jzoom.zoom.common.utils.CollectionUtils;
import com.jzoom.zoom.dao.Ar;
import com.jzoom.zoom.dao.Dao;
import com.jzoom.zoom.dao.Record;
import com.jzoom.zoom.dao.SqlBuilder.Sort;
import com.jzoom.zoom.dao.adapters.NameAdapter;
import com.jzoom.zoom.dao.meta.ColumnMeta;
import com.jzoom.zoom.dao.meta.TableMeta;
import com.jzoom.zoom.dao.transaction.Trans;
import com.jzoom.zoom.web.exception.StatusException;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.type.TypeReference;

import java.util.*;

/**
 * 表装饰管理器
 */
public class DecorationTableService {

	@Inject(value = "admin")
	private Dao admin;

	@Inject
	private ContentSercie contentSercie;

	public List<Record> getApis(Map<String, Object> params) {
		// 查询表
		List<Record> decos = getDecorated(params);


		return CollectionUtils.map(decos, new CollectionUtils.Converter<Record, Record>() {
			@Override
			public Record convert(Record data) {
				Record newRecord = new Record();
				newRecord.put("id", String.format("%s/%s/list", data.get("ds"), data.get("table")));
				return newRecord;
			}
		});
	}

	public List<Record> getDecorated(Map<String, Object> params) {
		Ar ar = admin.table("sys_deco_table")
				.select("target_table as table,comment,ds")
				.orderBy("target_table", Sort.ASC);
		AdminDaoModel.parseWhere(ar, params);
		// 查询表
		List<Record> decos = ar.find();
		return decos;
	}

	public void updateColumns(String ds, List<Map<String, Object>> columns) {
		Set<String> tables = new LinkedHashSet<String>();

		for (Map<String, Object> map : columns) {
			String table = (String) map.get("table");
			tables.add(table);
		}

		for (String table : tables) {
			admin.table("sys_decoration").where("ds", ds).where("target_table", table).delete();
		}

		for (Map<String, Object> map : columns) {
			String targetColumn = (String) map.remove("column");
			map.remove("name");
			String table = (String) map.remove("table");
			String type = (String) map.get("type");
			if (type == null) {
				// 默认string
				map.put("type", "string");
				// throw new StatusException.ApiError(String.format("column %s 的 type为空",
				// targetColumn));
			}

			admin.table("sys_decoration")
					.set("target_table", table)
					.set("target_column", targetColumn)
					.set("ds", ds)
					.setAll(map).insert();

		}
	}

	@Inject
	private GenerateService generateService;

	private void generatePages(String templateGroup, String ds, String table) {
		generateService.generatePage(templateGroup, ds, table);
	}

	@Trans
	public int update(String id, Boolean enableFilter, String tmpId, String ds, String name, String comment,
                      List<Map<String, Object>> columns, String select, List<Map<String, Object>> links,
                      List<Map<String, Object>> filterColumns, Map<String, Object> props) {
		Record record = admin.table("sys_deco_table").where("id", id).get();

		if (record == null) {
			throw new StatusException.ApiError("没有找到对应的记录" + id);
		}
		String displayColumns = getDisplayColumns(columns);
		admin.table("sys_deco_table").where("id", id)
				.set("ds", ds)
				.set("target_table", name)
				.set("comment", comment)
				.set("filter", enableFilter ? 1 : 0)
				.set("columns", select).set("display_columns", displayColumns)
                .set("filter_columns", JSON.stringify(filterColumns))
				.set("links", JSON.stringify(links))
				.set("tmp_id", tmpId)
				.set("props", JSON.stringify(props))
				.update();

		updateColumns(ds, columns);

		generatePages(tmpId, ds, name);

		removeCache(ds,name);

		return 1;
	}

	@Trans
	public int add(Boolean enableFilter, String tmpId, String ds, String name, String comment,
                   List<Map<String, Object>> columns, String select, List<Map<String, Object>> links,
                   List<Map<String, Object>> filterColumns, Map<String, Object> props) {
		if (comment == null) {
			throw new StatusException.ApiError("注释为空");
		}

		String displayColumns = getDisplayColumns(columns);
		// h2不支持insert or update ?
        if (admin.table("sys_deco_table")
                .where("ds", ds)
                .where("target_table", name)
                .set("comment", comment)
                .set("tmp_id", tmpId)
                .set("filter", enableFilter ? 1 : 0)
                .set("columns", select)
				.set("props", JSON.stringify(props))
                .set("links", JSON.stringify(links))
                .set("filter_columns", JSON.stringify(filterColumns))
				.set("display_columns", displayColumns).update() <= 0) {
			admin.table("sys_deco_table").set("ds", ds).set("target_table", name).set("comment", comment)
					.set("tmp_id", tmpId).set("filter", enableFilter ? 1 : 0).set("columns", select)
					.set("props", JSON.stringify(props))
                    .set("links", JSON.stringify(links))
                    .set("filter_columns", JSON.stringify(filterColumns))
					.set("display_columns", displayColumns).insert();

		}

		updateColumns(ds, columns);
		generatePages(tmpId, ds, name);
		removeCache(ds,name);
		return 1;
	}

	private String getDisplayColumns(List<Map<String, Object>> columns) {
		List<String> tables = new ArrayList<String>();
		for (Map<String, Object> map : columns) {
			if (Caster.to(map.get("selected"), boolean.class)) {
				String name = (String)map.get("name");
				tables.add(name);
			}
		}
		return StringUtils.join(tables, ",");
	}

	@Inject
	private DaoService daoService;

	void fillMeta(DecoTableVo decoTableVo, TableMeta tableMeta, NameAdapter nameAdapter, boolean hasLinks,boolean first) {
		if (decoTableVo.getComment() == null) {
			decoTableVo.setComment(tableMeta.getComment());
		}
		decoTableVo.setName(tableMeta.getName());
		ColumnMeta[] primaryKeys = tableMeta.getPrimaryKeys();
		String[] keys = new String[primaryKeys.length];
		int index = 0;
		for (ColumnMeta columnMeta : primaryKeys) {
			keys[index++] = nameAdapter
					.getFieldName(  (!first && hasLinks)  ? (tableMeta.getName() + "." + columnMeta.getName()) : columnMeta.getName());
		}
		decoTableVo.setPrimaryKeys(keys);
	}

	Map<String, DecoColumn> fillMetaColumns(DecoTableVo decoTableVo, TableMeta tableMeta, NameAdapter nameAdapter,
			boolean hasLinks,boolean first) {
		List<DecoColumn> list = decoTableVo.getColumns();
		if (list == null) {
			list = new ArrayList<DecoTableVo.DecoColumn>();
			decoTableVo.setColumns(list);
		}
		Map<String, DecoColumn> map = new HashMap<String, DecoColumn>();
		for (ColumnMeta columnMeta : tableMeta.getColumns()) {
			String fullColumnName = (!first && hasLinks) ?(tableMeta.getName() + "." + columnMeta.getName()):columnMeta.getName() ;
			DecoColumn decoColumn = new DecoColumn();
			decoColumn.setColumn(columnMeta.getName());
			decoColumn.setName(nameAdapter.getFieldName(fullColumnName));
			decoColumn.setOrgName(nameAdapter.getOrgFieldName(fullColumnName));
			decoColumn.setComment(columnMeta.getComment());
			decoColumn.setTable(tableMeta.getName());

			map.put(fullColumnName, decoColumn);
			list.add(decoColumn);
		}

		return map;
	}

	TableMeta getTableMeta(Dao dao, String table) {
		TableMeta data = dao.getDbStructFactory().getTableMeta(dao.ar(), table);
		dao.getDbStructFactory().fill(dao.ar(), data);
		return data;
	}

	/**
	 * 
	 * @param str
	 * @return
	 */
	private Set<String> split(String str) {
		// 并没有指定
		if (str == null || str.isEmpty())
			return null;
		String[] parts = str.split(",");
		return CollectionUtils.asSet(parts);
	}

	void fillTableDecoration(DecoTableVo decoTableVo,Record tableDecoration  ){
		decoTableVo.setId(tableDecoration.getString("id"));
		decoTableVo.setComment(tableDecoration.getString("comment"));
		decoTableVo.setTmpId(tableDecoration.getString("tmp_id"));
		String strFilterColumns = tableDecoration.getString("filter_columns");
		if (!StringUtils.isEmpty(strFilterColumns)) {
			try {
				List<DecoTableVo.DecoColumn> list = JSON.parse(strFilterColumns,
						new TypeReference<List<DecoTableVo.DecoColumn>>() {
						});
				decoTableVo.setFilterColumns(list);
			} catch (Throwable e) {

			}
		}
		String strProps = tableDecoration.getString("props");
		if (strProps != null) {
			try {
				decoTableVo.setProps(JSON.parse(strProps, Map.class));
			} catch (Exception e) {
				decoTableVo.setProps(Collections.EMPTY_MAP);
			}

		} else {
			decoTableVo.setProps(Collections.EMPTY_MAP);
		}
		decoTableVo.setDisplayColumns(split(tableDecoration.getString("display_columns")));

		setLinks(decoTableVo,tableDecoration  );
	}

	public void setLinks(DecoTableVo decoTableVo,Record tableDecoration){
		String strLinks = tableDecoration.getString("links");
		if (!StringUtils.isEmpty(strLinks)) {

			try {
				List<Link> links  = JSON.parse(strLinks, new TypeReference<List<DecoTableVo.Link>>() {
				});
				decoTableVo.setLinks(links);
			} catch (Throwable e) {

			}
		}
	}

	boolean fillTableDecoration(DecoTableVo decoTableVo, String ds, String table) {
		// 下发数据
		Record tableDecoration = admin.table("sys_deco_table").where("ds", ds).where("target_table", table).get();
		if (tableDecoration != null) {
			fillTableDecoration(decoTableVo,tableDecoration);
			return true;
		}
		return false;
	}

	@SuppressWarnings("unchecked")
	void fillColumnDecoration(String ds, String table, Map<String, DecoColumn> map, boolean hasLinks,boolean first) {
		List<Record> columns = admin.table("sys_decoration").where("ds", ds).where("target_table", table).find();

		if (columns.size() > 0) {
			for (Record column : columns) {
				String name = column.getString("target_column");
				String fullColumnName = (!first && hasLinks) ? (table + "." + name) : name;
				DecoColumn decoColumn = map.get(fullColumnName);
				if (decoColumn == null)
					continue;
				decoColumn.setComment(column.getString("comment"));
				decoColumn.setType(column.getString("type"));
				decoColumn.setSelected(column.getBoolean("selected"));

				String prop = column.getString("prop");
				if (!StringUtils.isEmpty(prop)) {
					try {
						Map<String, Object> propMap = JSON.parse(prop, Map.class);
						decoColumn.setProp(propMap);
					} catch (Exception e) {

					}

				}
			}
		}
	}

	/**
	 * 实时查询
	 * 
	 * @param ds
	 * @return
	 */
	public DecoTableVo getTable(String ds, String[] allTables) {
		Dao dao = daoService.getDao(ds);
		DecoTableVo decoTableVo = new DecoTableVo();
		String table = allTables[0];
		TableMeta tableMeta = getTableMeta(dao, table);
		boolean fillDecoration = fillTableDecoration(decoTableVo, ds, table);
		boolean hasLinks = false;
		if (allTables.length > 1) {
			hasLinks = true;
			fillMultiTables(dao, ds, allTables, decoTableVo, tableMeta, hasLinks, fillDecoration);
		} else {
			fillSingleTable(dao, ds, table, decoTableVo, tableMeta, hasLinks, fillDecoration);
		}

		decoTableVo.setDs(ds);

		return decoTableVo;
	}

	private void fillMultiTables(Dao dao, String ds, String[] allTables, DecoTableVo decoTableVo, TableMeta tableMeta,
			boolean hasLinks, boolean fillDecoration) {
		NameAdapter nameAdapter;
		nameAdapter = dao.getNameAdapterFactory().getNameAdapter(allTables);
		fillMeta(decoTableVo, tableMeta, nameAdapter, hasLinks,true);
		String table = allTables[0];
		Map<String, DecoColumn> map = fillMetaColumns(decoTableVo, tableMeta, nameAdapter, hasLinks,true);
		if (fillDecoration) {
			fillColumnDecoration(ds, table, map, hasLinks,true);
		}
		String[] linkTables = new String[allTables.length - 1];
		for (int i = 1; i < allTables.length; ++i) {
			linkTables[i - 1] = allTables[i];
		}
		for (String link : linkTables) {
			TableMeta linkTableMeta = getTableMeta(dao, link);
			map = fillMetaColumns(decoTableVo, linkTableMeta, nameAdapter, hasLinks,false);
			if (fillDecoration) {
				fillColumnDecoration(ds, link, map, hasLinks,false);
			}
		}
	}

	public DecoTableVo getTable(String id) {
		Record record = admin.table("sys_deco_table").where("id", id).get();
		return getTable(record.getString("ds"), record.getString("target_table"));
		//return admin.ar(DecoTableVo.class).get(id);
	}

	/**
	 * 获取表的生成配置,查询历史
	 * 
	 * @param ds
	 * @param table
	 * @return
	 */
	public DecoTableVo getTable(String ds, String table) {
		Dao dao = daoService.getDao(ds);
		DecoTableVo decoTableVo = new DecoTableVo();
		TableMeta tableMeta = getTableMeta(dao, table);
		boolean fillDecoration = fillTableDecoration(decoTableVo, ds, table);
		boolean hasLinks = false;
		List<Link> links = null;
		if (fillDecoration && (links = decoTableVo.getLinks()) != null && links.size() > 0) {
			String[] allTables = new String[links.size() + 1];
			allTables[0] = table;
			for (int i = 1; i < allTables.length; ++i) {
				String linkTable = links.get(i - 1).getTable();
				allTables[i] = linkTable;
			}
			hasLinks = true;
			fillMultiTables(dao, ds, allTables, decoTableVo, tableMeta, hasLinks, fillDecoration);

		} else {
			fillSingleTable(dao, ds, table, decoTableVo, tableMeta, hasLinks, fillDecoration);
		}

		decoTableVo.setDs(ds);
		//select
		for(DecoColumn decoColumn : decoTableVo.getColumns()){
			if(decoTableVo.shouldDisplay(decoColumn.getName())){
				decoColumn.setSelected(true);
			}else{
				decoColumn.setSelected(false);
			}
		}

		return decoTableVo;
	}

	void fillSingleTable(Dao dao, String ds, String table, DecoTableVo decoTableVo, TableMeta tableMeta,
			boolean hasLinks, boolean fillDecoration) {
		NameAdapter nameAdapter;
		nameAdapter = dao.getNameAdapterFactory().getNameAdapter(table);
		fillMeta(decoTableVo, tableMeta, nameAdapter, hasLinks,true);
		Map<String, DecoColumn> map = fillMetaColumns(decoTableVo, tableMeta, nameAdapter, hasLinks,true);
		if (fillDecoration) {
			fillColumnDecoration(ds, table, map, hasLinks,true);
		}
	}

	/**
	 * 通过模板分组获取所有模板
	 * 
	 * @param templateGroup
	 * @return
	 */
	public List<Record> getTempltesByTemplateGroupName(String templateGroup) {
		List<Record> list = admin.table("sys_template").where("type", templateGroup).find();

		return list;
	}

	public void insertOrUpdate(String name, String content) {
		int count = admin.table("sys_page").where("url", name).set("template", content).update();
		if (count <= 0) {
			admin.table("sys_page").set("url", name).set("template", content).insert();
		}

		contentSercie.updatePageForUrl(name);
	}

	/**
	 * 直接从数据库中加载配置，如果数据库中没有，那么返回null
	 * @param ds
	 * @param table
	 * @return
	 */
	//@Cache
	public DecoTableVo query(String ds, String table) {
		Record tableDecoration = admin.table("sys_deco_table")
			.where("ds", ds).where("target_table", table)
			.select("display_columns,links")
			.get();
		if(tableDecoration == null)return null;
		DecoTableVo decoTableVo = new DecoTableVo();
		decoTableVo.setDisplayColumns(split(tableDecoration.getString("display_columns")));
		setLinks(decoTableVo,tableDecoration);
		return decoTableVo;
	}

	//@CacheRemove
	public void removeCache(String ds,String table){

	}

	private List<DecoColumn> queryColumns(String ds, String table, boolean hasLinks){
		List<Record> columns = admin.table("sys_decoration").where("ds", ds).where("target_table", table).find();
		List<DecoColumn> result = new ArrayList<DecoColumn>(columns.size());
		for (Record column : columns) {
			DecoColumn decoColumn = new DecoColumn();
			decoColumn.setName(column.getString("target_column"));
			decoColumn.setComment(column.getString("comment"));
			decoColumn.setType(column.getString("type"));
			decoColumn.setSelected(column.getBoolean("selected"));
			String prop = column.getString("prop");
			if (!StringUtils.isEmpty(prop)) {
				try {
					Map<String, Object> propMap = JSON.parse(prop, Map.class);
					decoColumn.setProp(propMap);
				} catch (Exception e) {

				}
			}
		}
		return result;
	}
}
